#include <linux/fs.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/slab.h>

#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/gpio/consumer.h>

#include <linux/platform_device.h>

#define TAG "of_dts"
#define LOG_INF(format, args...)    pr_info(TAG "[%s:%d] " format, __func__, __LINE__, ##args)
#define LOG_ERR(format, args...)    pr_err(TAG "[%s:%d] " format, __func__, __LINE__, ##args)


#define BUFSIZE 256
char buf[BUFSIZE];

/**
 * 以树的形式打印dts结构
 */
void tree_dts(void)
{
	struct device_node * root = NULL;
	struct device_node * dn = NULL;
	struct	property * pp = NULL;

	root = of_find_node_by_path("/");
	// 遍历一个节点的全部子节点
	for(dn=NULL; ((dn=of_get_next_child(root, dn))!= NULL); ){
		// 打印节点的全部属性
		 for_each_property_of_node(dn, pp){
		 	LOG_INF("name=%s, value len=%d\n", pp->name, pp->length);
		 }
	}
}


// 属性值的读取
// of_property_read_u8/u16/u32/u64
void parse_value(void)
{
	char * test = "/test-node";
	char * bool_exist = "bool-value";
	char * bool_noexist = "none-value";
	struct of_phandle_args args;
	struct device_node * dn = NULL, *dn2=NULL, *dn3=NULL;
	struct property *prop;
	const __be32 *p;
	//u8 * bv = NULL;
	int lenp = 0, i = 0, j, rc;
	const void * pv ;
	u32 uv = 0;
	u32 * up = NULL;
	const char * sv;
	const char **out_strs;
	dn = of_find_node_by_path(test);
	if(!dn){
		LOG_ERR("%s not found\n", test);
		return;
	}
	if(of_property_read_bool(dn, bool_exist)){
		LOG_INF("%s OK\n", bool_exist);
	}
	if(!of_property_read_bool(dn, bool_noexist)){
		LOG_INF("%s OK\n", bool_noexist);
	}

	dn2 = of_find_compatible_node(NULL, NULL,"rockchip,test");
	if(!dn){
		LOG_ERR("rockchip,test not found\n");
	}
	LOG_INF("dn=%p, dn2=%p\n", dn, dn2);

	pv = of_get_property(dn2, "byte-array", &lenp);
	LOG_INF("lenp for byte-array is: %d\n", lenp);
	pv = of_get_property(dn2, "values", &lenp);
	LOG_INF("lenp for values is: %d\n", lenp);
	pv = of_get_property(dn2, "string-array", &lenp);
	LOG_INF("lenp for string-array is: %d\n", lenp);

	dn3 = of_parse_phandle(dn2, "ref_hall", 0);

	dn2 = of_find_compatible_node(NULL, NULL,"hall-mh248");
	LOG_INF("hall-mh248=%p, ref_hall=%p\n", dn2,dn3);

	lenp = of_count_phandle_with_args(dn, "list-phadles", "#list-cells");
	LOG_INF("list-phadles with: %d phandle\n", lenp);

	rc = of_parse_phandle_with_args(dn, "list-phadles", "#list-cells", 0, &args);
	dn2 = of_find_node_by_path("/node1");
	LOG_INF("node1=%p, rc.np=%p, rc.args_count=%d\n", dn2, args.np, args.args_count);

	rc = of_parse_phandle_with_args(dn, "list-phadles", "#list-cells", 1, &args);
	dn2 = of_find_node_by_path("/node2");
	LOG_INF("node2=%p, rc.np=%p, rc.args_count=%d\n", dn2, args.np, args.args_count);

	// traverse property value,
	// 1. find element count and fetch one by one
	lenp = of_property_count_elems_of_size(dn, "values", sizeof(u32));
	for(i=0, j=0; i<lenp; i++){
		of_property_read_u32_index(dn, "values", i, &uv);
		j += snprintf(buf+j, BUFSIZE, "%u ", uv);
	}
//	j += snprintf(buf+j, "\n");
	LOG_INF("values are: %s\n", buf);

	// alloc space for all values
	up = (u32*)kzalloc(lenp * sizeof(u32), GFP_KERNEL);
	if (pv != NULL){
		rc = of_property_read_variable_u32_array(dn, "values", up, lenp, 0);
		if(rc != lenp){
			LOG_ERR("expect %d, got %d\n", lenp, rc);
		}
	}

	// 3 foreach iterate
	j = 0;
	of_property_for_each_u32(dn, "values", prop, p, uv){
		j += snprintf(buf+j, BUFSIZE, "%u ", uv);
	}
	LOG_INF(" for each values are: %s\n", buf);

	// 4. string process
	lenp = of_property_count_strings(dn, "string-array");
	j = 0;
	for(i=0; i<lenp; ++i){
		of_property_read_string_index(dn, "string-array", i, &sv);
		j += snprintf(buf+j, BUFSIZE, "%s,", sv);
	}
	LOG_INF("read_string_index: lenp=%d, value=%s\n", lenp, buf);

	// 5. alloc space
	out_strs = (const char **)kzalloc(lenp * sizeof(const char *), GFP_KERNEL);
	of_property_read_string_array(dn, "string-array", out_strs, lenp);
	j = 0;
	for(i=0; i<lenp; ++i){
		j += snprintf(buf+j, BUFSIZE, "%s,", out_strs[i]);
	}
	LOG_INF("read_string_array: lenp=%d, value=%s\n", lenp, buf);
	// 6. iterate
	j = 0;
	of_property_for_each_string(dn, "string-array", prop, sv){
		j += snprintf(buf+j, BUFSIZE, "%s,", sv);
	}
	LOG_INF("for_each_string: lenp=%d, value=%s\n", lenp, buf);


}



void parse_gpio(void)
{
	struct device_node *dn2 = NULL;
	int t1, t2;
	dn2 = of_find_compatible_node(NULL, NULL,"rockchip,test");
	if(!dn2){
		LOG_ERR("find: rockchip,test fail\n");
		return;
	}
	t1 = of_get_named_gpio_flags(dn2, "t1-gpios", 0, NULL);
	if(gpio_is_valid(t1)){
		gpio_direction_output(t1, 1);
	}
	t2 = of_get_named_gpio_flags(dn2, "t2-gpios", 0, NULL);
	if(gpio_is_valid(t2)){
		gpio_direction_output(t2, 1);
	}
	LOG_INF("set %d and %d to 1 \n", t1, t2);
}


void parse_gpiod(struct device *dev)
{
	struct gpio_desc* t1, *t2;
	int err = 0;
	t1 = devm_gpiod_get_optional(dev, "t1", 0);
	if(IS_ERR(t1)){
		err = PTR_ERR(t1);
		LOG_ERR("get: t1 failed, err=%d\n", err);
	}else{
		gpiod_direction_output(t1, 1);
	}
	t2= devm_gpiod_get_optional(dev, "t2", 0);
	if(IS_ERR(t2)){
		err = PTR_ERR(t2);
		LOG_ERR("get: t2 failed, err=%d\n", err);
	}else{
		gpiod_direction_output(t2, 1);
	}
}


static int dts_demo_remove(struct platform_device *pdev)
{
	LOG_INF("enter");
	return 0;
}

static int dts_demo_probe(struct platform_device *pdev)
{
	LOG_INF("enter");
//	parse_gpiod(&pdev->dev);
parse_gpio();
	return 0;
}

static const struct of_device_id dts_demo_match[] = {
	{ .compatible = "rockchip,test",},
	{}
};

static struct platform_driver dts_demo_driver = {
	.probe = dts_demo_probe,
    .remove = dts_demo_remove,
    .driver = {
        .name = TAG,
 //       .module = THIS_MODULE,
        .of_match_table = dts_demo_match,
    },
};

static int __init demo_init(void)
{
	int err = 0;
	LOG_INF("enter");
	//parse_gpio();
	if((err = platform_driver_register(&dts_demo_driver))<0){
		LOG_ERR("platform_driver_register fail, err=%d\n", err);
		return err;
	}
	return 0;
}

static void __exit demo_exit(void)
{
	LOG_INF("enter");
	platform_driver_unregister(&dts_demo_driver);
	return;
}

module_init(demo_init);
module_exit(demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("tj<tj_wust@163.com>");